package com.bjy.qa.agent.transport.http;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bjy.qa.agent.exception.MyException;
import com.bjy.qa.agent.exception.UnsupportedFilterResultType;
import com.bjy.qa.agent.model.KeyValueStore;
import com.bjy.qa.agent.response.Response;
import com.bjy.qa.agent.tools.SpringTool;
import com.bjy.qa.agent.tools.file.DownloadTool;
import com.bjy.qa.agent.transport.command.HttpExecuteCommand;
import com.rslai.commons.http.RslaiHttpClient;
import com.rslai.commons.http.data.HttpParameter;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpVersion;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.AbstractHttpEntity;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class HttpService {
    private static final Logger logger = LoggerFactory.getLogger(HttpService.class);

    private RslaiHttpClient httpClient;
    private Map headers;
    private final String CONTENT_TYPE = "Content-Type";

    private static final HttpConf httpConf; // HttpClient 配置

    static {
        httpConf = SpringTool.getBean(HttpConf.class);
        logger.info("HttpClient 配置信息：{}", httpConf);
    }

    public HttpService() {
        httpClient = RslaiHttpClient.createDefaultClient(httpConf.getConnectionTimeout(), httpConf.getSocketTimeout(), httpConf.getMaxTotalConnections(), httpConf.getMaxPerRoute());
        httpClient.setRequestGlobalCookies();
        httpClient.allowCookiePolicy();
        HttpParams params = httpClient.getParams();
        params.setParameter("http.connection.stalecheck", httpConf.getConnectionStalecheck());
        params.setParameter("http.protocol.handle-redirects", httpConf.getProtocolHandleRedirects());
        httpClient.getParams().setParameter("http.protocol.version", httpVersion());
        httpClient.setDebug(httpConf.getDebug());
    }

    /**
     * http get 请求
     * @param resultId 结果id
     * @param caseId 用例id
     * @param ic 迭代次数（iteration count）
     * @param url 请求 url
     * @param params 请求参数列表
     * @return
     */
    public Response get(String resultId, String caseId, String ic, String url, List<KeyValueStore> params) {
        org.apache.http.HttpResponse httpResponse = doGet(resultId, caseId, ic, url, params);
        return response(httpResponse);
    }

    /**
     * http get 请求（返回流）
     * @param resultId 结果id
     * @param caseId 用例id
     * @param ic 迭代次数（iteration count）
     * @param url 请求 url
     * @param params 请求参数列表
     * @return
     */
    public Response getWithStream(String resultId, String caseId, String ic, String url, List<KeyValueStore> params) {
        org.apache.http.HttpResponse httpResponse = doGet(resultId, caseId, ic, url, params);
        return responseWithStream(httpResponse);
    }

    /**
     * 如果是 dto 类型的 BodyEntity 则转为普通 BodyEntity
     * @param params 请求参数列表
     * @return
     */
    public List<KeyValueStore> dtoToBodyEntity(List<KeyValueStore> params) {
        List<KeyValueStore> list = new LinkedList<KeyValueStore>();
        if ((params != null) && (params.size() == 2) && (containsKey(params, "param"))) { // 如果是 dto 类型的 BodyEntity 则转为普通 BodyEntity
            for (KeyValueStore kvs : params) {
                if (!"param".equals(kvs.getName())) {
                    Object obj = kvs.getValue();
                    if (obj instanceof String) {
                        list.add(new KeyValueStore("param", (String) obj));
                        return list;
                    } else {
                        JSONObject jsonObject = (JSONObject) JSON.toJSON(obj);
                        list.add(new KeyValueStore("param", jsonObject.toJSONString()));
                        return list;
                    }
                }
            }
        }
        return params;
    }

    /**
     * http entity 请求，例如：post 等
     * @param resultId 结果id
     * @param caseId 用例id
     * @param ic 迭代次数（iteration count）
     * @param url 请求 url
     * @param params 请求参数列表
     * @return
     */
    public Response entityRequest(String resultId, String caseId, String ic, String url, String method, List<KeyValueStore> params) {
        HttpRequestBase request = null;
        try {
            params = dtoToBodyEntity(params); // 如果是 dto 类型的 BodyEntity 则转为普通 BodyEntity
            if (isMultipartEntityRequest(method, params)) {
                request = doMultipartEntityRequest(resultId, caseId, ic, url, HttpExecuteCommand.fixMethod(method), params);
            } else {
                request = doEntityRequest(resultId, caseId, ic, url, HttpExecuteCommand.fixMethod(method), params);
            }
            org.apache.http.HttpResponse httpResponse = httpClient.execute(request);
            if (httpResponse != null && httpResponse.getStatusLine().getStatusCode() == 302) {
                // 返回 302 后自动跳转
                String redirectURL = httpResponse.getFirstHeader("location").getValue();
                if (!containProtocol(redirectURL)) {
                    redirectURL = getDomain(request.getURI().toString()) + redirectURL; // 如果302的location不包括域名，则从当前中request中得到域名添加上
                }
                return entityRequest(resultId, caseId, ic, redirectURL, "get", null); // 递归调用 entityRequest
            }
            return response(httpResponse);
        } catch (Exception e) {
            if (request != null) {
                request.abort();
            }
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 是否是 multipart entity 请求（method=binary，params=isBodyEntity）
     * @param method 请求方法
     * @param params 请求参数列表
     * @return
     */
    protected boolean isMultipartEntityRequest(String method, List<KeyValueStore> params) {
        return "binary".equalsIgnoreCase(method) && isBodyEntity(params);
    }

    /**
     * 判断 url 中是否包括协议头（https、ftp、http）
     * @param url url
     * @return true
     */
    private boolean containProtocol(String url) {
        String regex = "^(https|ftp|http)?://.*$";
        Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(url);
        return matcher.matches();
    }

    /**
     * 返回协议及域名
     * 例：http://www.baidu.com:443/xxx?aaa=xxxx 返回 http://www.baidu.com:443
     * @param url url
     * @return 协议及域名
     */
    private String getDomain(String url) {
        String re = "((http|ftp|https)://)(([a-zA-Z0-9._-]+)|([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}))(([a-zA-Z]{2,6})|(:[0-9]{1,4})?)";
        Pattern pattern = Pattern.compile(re, Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(url);
        String domain = "";
        if (matcher.matches()) {
            domain = url;
        } else {
            String[] split2 = url.split(re);
            if (split2.length > 1) {
                String substring = url.substring(0, url.length() - split2[1].length());
                domain = substring;
            } else {
                domain = split2[0];
            }
        }
        return domain;
    }

    /**
     * http entity 请求
     * @param resultId 结果id
     * @param caseId 用例id
     * @param ic 迭代次数（iteration count）
     * @param url 请求 url
     * @param method 请求方法
     * @param params 请求参数列表
     * @return
     */
    public org.apache.http.HttpResponse getHttpResponse(String resultId, String caseId, String ic, String url, String method, List<KeyValueStore> params) {
        HttpRequestBase request = null;
        try {
            request = doEntityRequest(resultId, caseId, ic, url, method, params);
            return httpClient.execute(request);
        } catch (Exception e) {
            if (request != null) {
                request.abort();
            }
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * http entity 请求（返回流）
     * @param resultId 结果id
     * @param caseId 用例id
     * @param ic 迭代次数（iteration count）
     * @param url 请求 url
     * @param method 请求方法
     * @param params 请求参数列表
     * @return
     */
    public Response entityRequestWithStream(String resultId, String caseId, String ic, String url, String method, List<KeyValueStore> params) {
        HttpRequestBase request = null;
        try {
            request = doEntityRequest(resultId, caseId, ic, url, method, params);
            org.apache.http.HttpResponse httpResponse = httpClient.execute(request);
            return responseWithStream(httpResponse);
        } catch (Exception e) {
            if (request != null) {
                request.abort();
            }
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * 将 httpResponse 转为 流式的 Response
     * @param httpResponse httpResponse
     * @return
     */
    private Response responseWithStream(org.apache.http.HttpResponse httpResponse) {
        int statusCode = httpResponse.getStatusLine().getStatusCode();
        InputStream content = null;
        try {
            content = httpResponse.getEntity().getContent();
            return new com.bjy.qa.agent.response.HttpResponse(Integer.valueOf(statusCode), content, httpResponse.getAllHeaders());
        } catch (IOException e) {
            IOUtils.closeQuietly(content);
        }
        throw new RuntimeException("读取网络流出错");
    }

    /**
     * 返回 http 版本
     * @return
     */
    private HttpVersion httpVersion() {
        if (httpConf.getVersion().equals("1.1")) {
            return HttpVersion.HTTP_1_1;
        }
        return HttpVersion.HTTP_1_0;
    }

    /**
     * 将 apache 的 HttpResponse 转为 自定义的 HttpResponse
     * @param httpResponse apache 的 HttpResponse
     * @return 自定义的 HttpResponse
     */
    private com.bjy.qa.agent.response.HttpResponse response(org.apache.http.HttpResponse httpResponse) {
        return new com.bjy.qa.agent.response.HttpResponse(Integer.valueOf(httpResponse.getStatusLine().getStatusCode()), getContent(httpResponse), httpResponse.getAllHeaders());
    }

    /**
     * 发送 http client 的请求（EntityRequest）
     * @param resultId 结果id
     * @param caseId 用例id
     * @param ic 迭代次数（iteration count）
     * @param url 请求 url
     * @param method 请求方法
     * @param params 请求参数列表
     * @return
     * @throws Exception
     */
    private HttpRequestBase doEntityRequest(String resultId, String caseId, String ic, String url, String method, List<KeyValueStore> params) throws Exception {
        AbstractHttpEntity entity = null;
        if (isBodyEntity(params)) {
            Object entityBody = getEntityBody(params);
            if (isByteArray(entityBody))
                entity = new ByteArrayEntity(getByteArray((Object[]) entityBody));
            else if (isString(entityBody))
                entity = new StringEntity((String) entityBody, "UTF-8");
            else {
                throw new UnsupportedFilterResultType(String.format("filter result type %s is unsupported.", new Object[]{entityBody.getClass().getCanonicalName()}));
            }
            HttpRequestBase request = doEntityRequest(resultId, caseId, ic, url, method, entity);
            Header[] headers = request.getHeaders("Content-Type");
            if (headers.length == 0) {
                request.setHeader("Content-Type", "application/json");
            }
            return request;
        }
        HttpParameter httpParameter = getParameters(convertRequestParameter(params));
        entity = new UrlEncodedFormEntity(httpParameter.getNameValuePairs(), "UTF-8");
        return doEntityRequest(resultId, caseId, ic, url, method, entity);
    }

    /**
     * 发送 http client 的请求（MultipartEntityRequest）
     * @param resultId 结果id
     * @param caseId 用例id
     * @param ic 迭代次数（iteration count）
     * @param url 请求 url
     * @param method 请求方法
     * @param params 请求参数列表
     * @return
     * @throws Exception
     */
    private HttpRequestBase doMultipartEntityRequest(String resultId, String caseId, String ic, String url, String method, List<KeyValueStore> params) throws Exception {
        HttpEntity entity = null;
        if (isBodyEntity(params)) {
            Object entityBody = getEntityBody(params);
            if (isString(entityBody)) {
                entity = MultipartEntity((String) entityBody, "UTF-8");
            } else {
                throw new UnsupportedFilterResultType(String.format("filter result type %s is unsupported.", new Object[]{entityBody.getClass().getCanonicalName()}));
            }
            HttpRequestBase request = doMultipartEntityRequest(resultId, caseId, ic, url, method, entity);
            return request;
        }
        throw new MyException("multipart type Entity Request Must be of Body Entity.");
    }

    /**
     * 使用指定的内容和字符集创建 MultipartEntity
     * @param bodyString 指定的内容
     * @param charset 字符集
     * @return
     * @throws UnsupportedCharsetException
     */
    private HttpEntity MultipartEntity(String bodyString, String charset) throws UnsupportedCharsetException {
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        Charset chars = Charset.forName(charset);
        builder.setCharset(chars);

        JSONObject bodyJsonObject = JSONObject.parseObject(bodyString);

        // 遍历参数，添加 form-data
        for (Map.Entry<String ,Object> entry : bodyJsonObject.entrySet()) {
            if (!"fileList".equalsIgnoreCase(entry.getKey())) {
                switch (entry.getValue().getClass().getSimpleName()) {
                    case "String":
                        builder.addTextBody(entry.getKey(), (String) entry.getValue(), ContentType.MULTIPART_FORM_DATA);
                        break;
                    case "Integer":
                        builder.addTextBody(entry.getKey(), Integer.toString((Integer) entry.getValue()), ContentType.MULTIPART_FORM_DATA);
                        break;
                    case "Long":
                        builder.addTextBody(entry.getKey(), Long.toString((Long) entry.getValue()), ContentType.MULTIPART_FORM_DATA);
                        break;
                    case "Double":
                        builder.addTextBody(entry.getKey(), Double.toString((Double) entry.getValue()), ContentType.MULTIPART_FORM_DATA);
                        break;
                    case "Float":
                        builder.addTextBody(entry.getKey(), Float.toString((Float) entry.getValue()), ContentType.MULTIPART_FORM_DATA);
                        break;
                    case "Boolean":
                        builder.addTextBody(entry.getKey(), Boolean.toString ((Boolean) entry.getValue()), ContentType.MULTIPART_FORM_DATA);
                        break;
                    default:
                        throw new MyException(String.format("parameter types %s is Unsupported", entry.getKey().getClass().getCanonicalName()));
                }
            }
        }

        // 遍 bodyJsonObject 添加 file
        JSONArray fileList = bodyJsonObject.getJSONArray("fileList");
        if (fileList != null) {
            fileList.forEach(o -> {
                JSONObject fileObject = (JSONObject) o;
                String name = fileObject.getString("name");
                String fileName = fileObject.getString("fileName");
                String file = fileObject.getString("file");

                if (StringUtils.isBlank(name)) {
                    throw new MyException("BINARY 类型数据中 fileList.name 不能为空。");
                }
                if (StringUtils.isBlank(file)) {
                    throw new MyException("BINARY 类型数据中 fileList.file 不能为空。");
                }
                if (StringUtils.isBlank(fileName)) {
                    fileName = new File(fileName).getName();
                }

                // 准备待上传的文件，如果是网络文件，则先下载到本地
                File localFile = null;
                if (containProtocol(file)) {
                    try {
                        localFile = DownloadTool.download(file);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                } else {
                    localFile = new File(file);
                }

                builder.addBinaryBody(name, localFile, getFileContentType(fileName), fileName);
            });
        }

        return builder.build();
    }

    /**
     * 获取指定文件名的 ContentType
     * @param fileName 文件名
     * @return
     */
    private ContentType getFileContentType(String fileName) {
        ContentType contentType = null;
        switch (fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase()) {
            case "bmp":
                contentType = ContentType.IMAGE_BMP;
                break;
            case "gif":
                contentType = ContentType.IMAGE_GIF;
                break;
            case "jpg":
            case "jpeg":
                contentType = ContentType.IMAGE_JPEG;
                break;
            case "png":
                contentType = ContentType.IMAGE_PNG;
                break;
            case "tif":
            case "tiff":
                contentType = ContentType.IMAGE_TIFF;
                break;
            case "txt":
                contentType = ContentType.TEXT_PLAIN;
                break;
            case "xml":
                contentType = ContentType.TEXT_XML;
                break;
            case "htm":
            case "html":
                contentType = ContentType.TEXT_HTML;
                break;
            case "pdf":
                contentType = ContentType.create("application/pdf");
                break;
            case "doc":
                contentType = ContentType.create("application/msword");
                break;
            case "docx":
                contentType = ContentType.create("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
                break;
            case "xls":
                contentType = ContentType.create("application/vnd.ms-excel");
                break;
            case "xlsx":
                contentType = ContentType.create("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
                break;
            case "ppt":
                contentType = ContentType.create("application/vnd.ms-powerpoint");
                break;
            case "pptx":
                contentType = ContentType.create("application/vnd.openxmlformats-officedocument.presentationml.presentation");
                break;
            case "zip":
                contentType = ContentType.create("application/zip");
                break;
            case "rar":
                contentType = ContentType.create("application/x-rar");
                break;
            case "7z":
                contentType = ContentType.create("application/x-7z-compressed");
                break;
            case "gz":
                contentType = ContentType.create("application/x-gzip");
                break;
            case "tar":
                contentType = ContentType.create("application/x-tar");
                break;
            case "bz2":
                contentType = ContentType.create("application/x-bzip2");
                break;
            case "tgz":
                contentType = ContentType.create("application/gzip");
                break;
            case "apk":
                contentType = ContentType.create("application/vnd.android.package-archive");
                break;
            case "jar":
                contentType = ContentType.create("application/java-archive");
                break;
            case "bin":
                contentType = ContentType.create("application/macbinary");
                break;
            case "php":
                contentType = ContentType.create("text/php");
                break;
            case "js":
                contentType = ContentType.create("text/javascript");
                break;
            case "css":
                contentType = ContentType.create("text/css");
                break;
            case "py":
                contentType = ContentType.create("text/x-python-script");
                break;
            case "sh":
                contentType = ContentType.create("text/x-sh");
                break;
            case "psd":
                contentType = ContentType.create("image/vnd.adobe.photoshop");
                break;
//            case "exe":
//            case "msi":
//            case "ipa":
//            case "iso":
//            case "img":
//            case "war":
//            case "class":
//            case "ear":
//            case "dll":
//            case "so":
//            case "lib":
//            case "obj":
//            case "o":
//            case "a":
//            case "dat":
//            case "db":
//            case "dbf":
//            case "mdb":
//            case "sql":
//            case "log":
//            case "jsp":
//            case "asp":
//            case "aspx":
//            case "cs":
//            case "c":
//            case "cpp":
//            case "h":
//            case "hpp":
//            case "less":
//            case "sass":
//            case "scss":
//            case "java":
//            case "go":
//            case "lua":
//            case "bat":
//            case "ps1":
//            case "psm1":
//            case "psd1":
//            case "ps1xml":
//            case "psc1":
//            case "psc2":
//            case "mof":
//            case "pssc":
//            case "cdxml":
//            case "xaml":
            default:
                contentType = ContentType.DEFAULT_BINARY;
        }

        return contentType;
    }

    /**
     * 将请求参数从 Object[] 转为 byte[]
     * @param entityBody 请求参数
     * @return byte[]
     */
    public byte[] getByteArray(Object[] entityBody) {
        byte[] result = new byte[entityBody.length];
        for (int i = 0; i < entityBody.length; i++) {
            result[i] = ((Byte) entityBody[i]).byteValue();
        }
        return result;
    }

    /**
     * 判断是否为 String 类型
     * @param value 需要判断的对象
     * @return
     */
    public boolean isString(Object value) {
        return (value != null) && ((value instanceof String));
    }

    /**
     * 判断是否为 byte[] 类型
     * @param value 需要判断的对象
     * @return
     */
    public boolean isByteArray(Object value) {
        if ((value != null) && (value.getClass().isArray())) {
            int length = Array.getLength(value);
            for (int i = 0; i < length; i++) {
                Object o = Array.get(value, i);
                if (!(o instanceof Byte)) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

    /**
     * 返回请求体
     * @param params 请求参数
     * @return HttpParameter
     */
    public Object getEntityBody(List<KeyValueStore> params) {
        if (params != null) {
            for (KeyValueStore kvs : params) {
                if ("param".equals(kvs.getName())) {
                    return kvs.getValue();
                }
            }
        }
        return null;
    }

    /**
     * 判断是否为请求体
     * @param params 请求参数
     * @return
     */
    public boolean isBodyEntity(List<KeyValueStore> params) {
        return (params != null) && (params.size() == 1) && (containsKey(params, "param"));
    }

    /**
     * 判断是否包含某个参数
     * @param params 请求参数
     * @param param 参数名
     * @return
     */
    public boolean containsKey(List<KeyValueStore> params, String param) {
        for (KeyValueStore kvs : params) {
            if (param.equals(kvs.getName())) {
                return true;
            }
        }
        return false;
    }

    class HttpEntityRequest extends HttpEntityEnclosingRequestBase {
        final String method;

        public String getMethod() {
            return method;
        }

        HttpEntityRequest(String method) {
            super();
            this.method = method.toUpperCase();
        }
    }

    /**
     * 生成 http 请求（HttpRequestBase）
     * @param resultId 结果id
     * @param caseId 用例id
     * @param ic 迭代次数（iteration count）
     * @param url 请求 url
     * @param method 请求方法
     * @param entity 请求体（AbstractHttpEntity）
     * @return
     */
    private HttpRequestBase doEntityRequest(String resultId, String caseId, String ic, String url, String method, AbstractHttpEntity entity) {
        HttpEntityEnclosingRequestBase request = new HttpEntityRequest(method);

        String host = DNS.getHost(url);
        url = urlWithIp(url);
        request.setURI(URI.create(url));
        request.setHeader("Host", host);
        setHeaders(request);
        setCaseIdToHeader(request, resultId, caseId, ic);
        request.setEntity(entity);
        return request;
    }

    /**
     * 生成 http 请求（HttpRequestBase）
     * @param resultId 结果id
     * @param caseId 用例id
     * @param ic 迭代次数（iteration count）
     * @param url 请求 url
     * @param method 请求方法
     * @param entity 请求体（HttpEntity）
     * @return
     */
    private HttpRequestBase doMultipartEntityRequest(String resultId, String caseId, String ic, String url, String method, HttpEntity entity) {
        HttpEntityEnclosingRequestBase request = new HttpEntityRequest(method);

        String host = DNS.getHost(url);
        url = urlWithIp(url);
        request.setURI(URI.create(url));
        request.setHeader("Host", host);
        setHeaders(request);
        setCaseIdToHeader(request, resultId, caseId, ic);
        request.setEntity(entity);
        return request;
    }

    /**
     * 设置 http 追踪信息，会在 http head 请求头中添加追踪信息，例如：x-qap-request-id: resultId-xxx-caseId-xxx
     * @param request request
     * @param resultId 结果 id
     * @param caseId 用例 id
     * @param ic 迭代次数（iteration count）
     */
    private void setCaseIdToHeader(HttpRequestBase request, String resultId, String caseId, String ic) {
        if (httpConf.getTrackingMarkers()) {
            request.setHeader("x-qap-request-id", "resultId-" + resultId + "-caseId-" + caseId + "-ic-" + ic);
        }
    }

    /**
     * 设置 http 请求头
     * @param request
     */
    public void setHeaders(HttpRequestBase request) {
        Iterator i$;
        if ((headers != null) && (headers.size() > 0))
            for (i$ = headers.keySet().iterator(); i$.hasNext(); ) {
                Object key = i$.next();
                if ((key != null) && (headers.get(key) != null)) {
                    request.setHeader(key.toString(), headers.get(key).toString());
                }
            }
    }

    /**
     * http get 请求（返回的是 apache 的 HttpResponse）
     * @param resultId 结果 id
     * @param caseId 用例 id
     * @param ic 迭代次数（iteration count）
     * @param url 请求 url
     * @param params 请求参数
     * @return
     */
    public org.apache.http.HttpResponse doGet(String resultId, String caseId, String ic, String url, List<KeyValueStore> params) {
        HttpGet httpGet = null;
        try {
            String host = DNS.getHost(url);
            url = urlWithIp(url);
            httpGet = new HttpGet(getHttpGetURL(url, params));
            httpGet.setHeader("Host", host);
            setHeaders(httpGet);
            setCaseIdToHeader(httpGet, resultId, caseId, ic);
            return httpClient.execute(httpGet);
        } catch (IOException e) {
            if (httpGet != null) {
                httpGet.abort();
            }
            throw new RuntimeException(String.format("访问(%s)出错", new Object[]{url}), e);
        }
    }

    /**
     * 从 apache 的 HttpResponse 中返回 content
     * @param httpResponse apache 的 HttpResponse
     * @return
     */
    public String getContent(org.apache.http.HttpResponse httpResponse) {
        try {
            HttpEntity entity = httpResponse.getEntity();
            return EntityUtils.toString(entity, "UTF-8");
        } catch (IOException e) {
            throw new RuntimeException("读取网络流出错", e);
        }
    }

    /**
     * 生成 get 方法的请求 url
     * 例如：urr=http://www.baidu.com/aaa parameters=[param1=value1, param2=value2] 生成的 url 为：http://www.baidu.com/aaa?param1=value1&param2=value2
     * @param url 请求 url
     * @param parameters 请求参数
     * @return 生成的 url
     */
    public String getHttpGetURL(String url, List<KeyValueStore> parameters) {
        if ((parameters == null) || (StringUtils.isEmpty(url))) {
            return url;
        }
        StringBuilder sb = new StringBuilder();
        sb.append(url);
        if (!StringUtils.endsWith(url, "?")) {
            sb.append("?");
        }
        for (KeyValueStore kvs : parameters) {
            String name = kvs.getName();
            String value = encode((String) kvs.getValue());
            sb.append(name).append("=").append(value).append("&");
        }
        String result = sb.toString();
        if (result.endsWith("&")) {
            result = result.substring(0, result.length() - 1);
        }
        return result;
    }

    /**
     * 返回 ip 地址形式的 url（只有本地 hosts 中配置过的域名，才会返回 ip 形式）
     * 例：http://www.baidu.com/aaa 转为 http://172.20.2.3/aaa
     * @param url 请求 url
     * @return
     */
    public String urlWithIp(String url) {
        if (StringUtils.isBlank(url)) return url;
        String host = DNS.getHost(url);
        String ip = DNS.dnsLookup(host);
        if (!host.equalsIgnoreCase(ip))
            url = url.replace(host, ip);
        return fixUrlPrefix(url);
    }

    /**
     * 检查输入的 url，前缀是否是 http:// 或 https://，不是前边补 http://
     * @param url 输入的 url
     * @return
     */
    private String fixUrlPrefix(String url) {
        boolean hasPrefix = StringUtils.startsWithAny(StringUtils.lowerCase(url), new String[]{"http://", "https://"});
        if (!hasPrefix) {
            logger.warn("Your url should with a protocol:{}", url);
            return String.format("http://%s", new Object[]{url});
        }
        return url;
    }

    /**
     * 对传入的内容进行 url 编码
     * @param value 需要编码的内容
     * @return 编码后的内容
     */
    private String encode(String value) {
        try {
            if (value != null)
                return URLEncoder.encode(value, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            logger.error(e.getMessage(), e);
            throw new RuntimeException(e.getMessage(), e);
        }
        return null;
    }

    /**
     * 将参数转换为 map 形式，如果参数中有多个相同的 key，则将其转换为 list 形式存入一个 map 中
     * @param parameters 参数
     * @return
     */
    private Map<String, List<String>> convertRequestParameter(List<KeyValueStore> parameters) {
        Map param = new HashMap();
        if ((parameters != null) && (!parameters.isEmpty())) {
            for (KeyValueStore kvs : parameters) {
                List list = (List) param.get(kvs.getName());
                if (list == null) {
                    list = new ArrayList();
                    Object obj = kvs.getValue();
                    if (obj instanceof String) {
                        list.add((String) kvs.getValue());
                    } else if (obj instanceof Map) {
                        list.add((Map) kvs.getValue());
                    }
                    param.put(kvs.getName(), list);
                } else {
                    Object obj = kvs.getValue();
                    if (obj instanceof String) {
                        list.add((String) obj);
                    } else if (obj instanceof Map) {
                        list.add((Map) obj);
                    }
                }
            }
        }
        return param;
    }

    /**
     * 将 map 形式的参数转换为 HttpParameter 对象
     * @param parameters
     * @return
     */
    public HttpParameter getParameters(Map<String, List<String>> parameters) {
        HttpParameter httpParameter = new HttpParameter();
        if ((parameters == null) || (parameters.isEmpty())) {
            return httpParameter;
        }
        Map.Entry entry;
        for (Iterator i$ = parameters.entrySet().iterator(); i$.hasNext(); ) {
            entry = (Map.Entry) i$.next();
            for (Object value : (List) entry.getValue()) {
                if (value instanceof String) {
                    httpParameter.put((String) entry.getKey(), (String) value);
                } else if (value instanceof Map) {
                    for (Object obj : ((Map) value).entrySet()) {
                        Map.Entry valEntry = (Map.Entry) obj;
                        httpParameter.put((String) (valEntry).getKey(), (String) (valEntry).getValue());
                    }
                }
            }
        }
        return httpParameter;
    }

    /**
     * 设置 http 请求头
     * @param headers
     */
    public void setHeaders(Map headers) {
        if (headers != null && headers.size() != 0)
            this.headers = headers;
    }

    /**
     * 向 headers 中添加一个 Header
     * @param key key
     * @param value value
     */
    public void addHeader(String key, Object value) {
        if (this.headers == null) {
            this.headers = new JSONObject();
        }
        this.headers.put(key, value);
    }

    /**
     * 从 headers 中移除指定的 header
     * @param header 需要移除的 header
     */
    public void removeHeader(String header) {
        if (StringUtils.isBlank(header)) return;
        if ((headers == null) || (headers.size() <= 0)) return;
        if ("Cookie".equals(header)) {
            httpClient.setCookieStore(new BasicCookieStore());
        }
        for (Iterator i$ = headers.keySet().iterator(); i$.hasNext(); ) {
            Object key = i$.next();
            if (key != null) {
                String keyStr = key.toString();

                if (keyStr.equals(header)) {
                    headers.remove(header);
                    return;
                }
            }
        }
    }
}
